﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Net;
using System.Net.Sockets;

using System.Threading;

namespace FSLS
{
    public class FSLSServer
    {
        TcpListener _listener = null;
        Thread _watchLoop = null;

        Dictionary<int, FSLSClient> _clients = null;

        #region events
        public delegate void ServerStatusChangeDelegate(int newStatus);
        public delegate void ClientActivityDelegate(int clientId);

        public event ServerStatusChangeDelegate ServerStatusChanged;
        public event ClientActivityDelegate NewClientAccepted;
        public event ClientActivityDelegate ClientDisconnected;
        #endregion

        public FSLSServer()
        {
            _listener = new TcpListener(IPAddress.Any, 9367);
            _watchLoop = new Thread(new ThreadStart(watchForClients));
            _watchLoop.IsBackground = true;
            _watchLoop.Name = "Listener Thread";

            _clients = new Dictionary<int, FSLSClient>();
        }

        public void Start()
        {
            try
            {
                _listener.Start();
                _watchLoop.Start();
            }
            catch (Exception ex)
            {
                throw ex;
            }

            ServerStatusChanged(1);
        }

        public Dictionary<int, FSLSClient> Clients
        {
            get { return _clients; }
        }

        void watchForClients()
        {
            try
            {
                while (true)
                {
                    // Is someone waiting to connect?
                    if (_listener.Pending())
                    {
                        // Accept their connection and assign them an id.
                        ManualResetEvent mre = new ManualResetEvent(false);
                        int clientId = getNextClientId();
                        FSLSClient client = new FSLSClient(_listener.AcceptTcpClient(), clientId, mre);
                        client.ConnectionDropped += TossClient;

                        // Add them to the thread queue.
                        _clients.Add(clientId, client);
                        ThreadPool.QueueUserWorkItem(client.ClientMonitorCallback, clientId);

                        // Alert everyone of the news!
                        NewClientAccepted(clientId);
                    }
                }
            }
            catch (ThreadAbortException)
            {
                _watchLoop = null;
                _listener.Stop();
                _listener = null;
            }
        }

        void TossClient(int clientId)
        {
            if (Clients.ContainsKey(clientId))
                Clients.Remove(clientId);

            ClientDisconnected(clientId);
        }

        int getNextClientId()
        {
            int x = 1;
            while (_clients.ContainsKey(x))
                x++;

            return x;
        }
    }
}
